Lorenz curve & Gini coefficient
a Gini coefficient of 0.56 is quite high, which means that only a top few producers are responsible for a large amount of the total SUP waste contribution, in other words 10% are responsible for 44% of the waste
name = c('total_contribution_to_sup_waste', 'Producers')
df <- plastic %>% rename(value = total_contribution_to_sup_waste) %>% select(value)
library(gglorenz) #transformations for plotting Lorenz curve, https://github.com/jjchern/gglorenz
lorenzcurve <- df %>%
ggplot(aes(value)) +
stat_lorenz(desc = FALSE) +
coord_fixed() +
geom_abline(linetype = 'dashed') +
theme_minimal() +
hrbrthemes::scale_x_percent() +
hrbrthemes::scale_y_percent() +
# hrbrthemes::theme_ipsum_rc() +
annotate_ineq(df$value) +
ggtitle(paste("Lorenz curve for", name[1], sep=" "))
lorenzcurve <- ggplotly(lorenzcurve) %>% layout(yaxis = list(title = paste("cumulative percentage of", name[1], sep=" ")), xaxis = list(title = paste("cumulative percentage of", name[2], sep=" ")))
lorenzcurve
name = c('flexible_format_contribution_to_sup_waste', 'rigid_format_contribution_to_sup_waste', 'Producers', 'flexible', 'rigid')
df <- plastic %>% rename(flexible = flexible_format_contribution_to_sup_waste, rigid = rigid_format_contribution_to_sup_waste) %>% select(flexible, rigid) %>% pivot_longer(cols = c(flexible,rigid))
library(gglorenz) #transformations for plotting Lorenz curve, https://github.com/jjchern/gglorenz
gg_color_hue <- function(n) {
hues = seq(15, 375, length = n + 1)
hcl(h = hues, l = 65, c = 100)[1:n]
}
lorenzcurve <- df %>%
ggplot(aes(x = value, color = name)) +
stat_lorenz(desc = FALSE) +
coord_fixed() +
geom_abline(linetype = 'dashed') +
theme_minimal() +
hrbrthemes::scale_x_percent() +
hrbrthemes::scale_y_percent() +
# hrbrthemes::theme_ipsum_rc() +
annotate_ineq(filter(df, name == name[4])$value, y = 0.95, colour = gg_color_hue(2)[1]) +
annotate_ineq(filter(df, name == name[5])$value, y = 0.90, colour = gg_color_hue(2)[2]) +
ggtitle(paste("compare Lorenz curve of", name[1], "and", name[2], sep=" "))
lorenzcurve <- ggplotly(lorenzcurve) %>% layout(yaxis = list(title = paste("cumulative percentage of<br>", name[1], "<br>", name[2], sep="")), xaxis = list(title = paste("cumulative percentage of", name[2], sep=" ")))
lorenzcurve
| principal component analysis colored by self organizing map cluster |
I need more knowledge how to work with and interpret SOM and PCA, maybe also not enough observations in data set https://iamciera.github.io/SOMexample/html/SOM_RNAseq_tutorial_part2a_SOM.html
name = c('polymer_producer')
df <- plastic %>% select(- total_waste_div_production, -rank, -no_of_assets, -total_contribution_to_sup_waste) %>% # removed variables which are depended on each other
rename( product = production_of_in_scope_polymers,
flexible = flexible_format_contribution_to_sup_waste,
rigid = rigid_format_contribution_to_sup_waste)
library(kohonen) # functions to train self-organising maps (SOMs)
# setup for pca
scale_data <- as.matrix(t(scale(t(df[, !names(df) %in% name])))) # We need to normalize the data based on scale function because the variables are different scales; Normalization means subtracting mean from each observation and dividing with standard deviation. Check all the variables mean values are zero now
head(scale_data)
product flexible rigid
[1,] 1.0838622 -0.1970659 -0.8867964
[2,] 1.0301899 -0.0633963 -0.9667936
[3,] 1.1172018 -0.3058262 -0.8113756
[4,] 0.6859477 -1.1474034 0.4614557
[5,] 1.1208971 -0.3202563 -0.8006408
[6,] 1.0995250 -0.2443389 -0.8551861
# principle component analysis
pca <- prcomp(scale_data, scale=TRUE)
summary(pca)
Importance of components:
PC1 PC2 PC3
Standard deviation 1.6591 0.49736 4.375e-16
Proportion of Variance 0.9175 0.08246 0.000e+00
Cumulative Proportion 0.9175 1.00000 1.000e+00
# add back to original so everything is together
pca_scores <- data.frame(pca$x)
data_val <- cbind(df, pca_scores)
pca_plot <- ggplot(data_val, aes(x = PC1, y = PC2)) +
geom_rug(alpha = 0.5) + # two 1d marginal distributions, display individual cases so are best used with smaller datasets
geom_density_2d(alpha = 0.2, bins = 4) +# 2D kernel density estimation using MASS::kde2d() and display the results with contours
geom_point(alpha = 0.75) + # point geom is used to create scatterplots
theme_minimal()
pca_plot <- ggplotly(pca_plot) %>% layout()
pca_plot
# clustering is performed using the som() function on the scaled gene expression values.
set.seed(3)
# define a grid for the SOM and train
grid_size <- ncol(scale_data)
som_grid <- somgrid(xdim = grid_size, ydim = grid_size, topo = 'hexagonal')
som_model <- som(scale_data, grid = som_grid)
summary(som_model)
SOM of size 3x3 with a hexagonal topology and a bubble neighbourhood function.
The number of data layers is 1.
Distance measure(s) used: sumofsquares.
Training data included: 100 objects.
Mean distance to the closest unit in the map: 0.007.
# generate som plots after training
plot(som_model, type = 'mapping')

plot(som_model, type = 'codes')

# plot(som_model, type = 'counts')
# plot(som_model, type = 'dist.neighbours')
# plot(som_model, type = 'quality')
# plot(som_model, type = 'changes')
# further split the clusters into a smaller set of clusters using hierarchical clustering.
som_cluster <- cutree(hclust(dist(som_model$codes[[1]])), 2) # use hierarchical clustering to cluster the codebook vectors
plot(som_model, type="mapping", bgcol = som_cluster, main = "Clusters")
add.cluster.boundaries(som_model, som_cluster)

# attach the hierchal cluster to the larger dataset data_val.
gridSquare <- grid_size * grid_size
som_clusterKey <- data.frame(som_cluster)
som_clusterKey$unit_classif <- c(1:gridSquare)
data_val <- cbind(data_val,som_model$unit.classif,som_model$distances) %>% rename(unit_classif = 'som_model$unit.classif', distances = 'som_model$distances')
data_val <- merge(data_val, som_clusterKey, by.x = "unit_classif" )
head(data_val)
# plot pca with colored clusters
pcasom_plot <- ggplot(data_val, aes(x = PC1, y = PC2, color = factor(som_cluster), text = polymer_producer)) +
geom_rug(alpha = 0.5) + # two 1d marginal distributions, display individual cases so are best used with smaller datasets
geom_point(alpha = 0.75) + # point geom is used to create scatterplots
theme_minimal()
pcasom_plot <- ggplotly(pcasom_plot) %>% layout()
pcasom_plot
# two variables, continuous x, continuous y, show trend and distribution
name = c('production_of_in_scope_polymers', 'total_contribution_to_sup_waste')
df <- merge(plastic, data_val, by.x = 'polymer_producer')
df <- df %>% rename(x = production_of_in_scope_polymers, y = total_contribution_to_sup_waste, cluster = som_cluster, text = polymer_producer) %>% select(x, y, cluster, text)
# https://ggplot2.tidyverse.org/reference/geom_smooth.html
point_plot <- df %>%
ggplot(aes(x = x, y = y, color = factor(cluster))) +
# geom_jitter(alpha = 0.5, size = 1) +
geom_rug(alpha = 0.5) + # two 1d marginal distributions, display individual cases so are best used with smaller datasets
geom_density_2d(alpha = 0.2, bins = 4) +# 2D kernel density estimation using MASS::kde2d() and display the results with contours
geom_smooth(fill = "grey90") + # aids the eye in seeing patterns in the presence of overplotting
geom_point(aes(text = text), alpha = 0.75) + # point geom is used to create scatterplots
theme_minimal() +
ggtitle(paste("trend of", name[2], "over", name[1], sep=" "))
Ignoring unknown aesthetics: text
point_plot <- ggplotly(point_plot) %>% layout(xaxis = list(showticklabels = FALSE))
`geom_smooth()` using method = 'loess' and formula 'y ~ x'
x_density_plot <- df %>%
ggplot(aes(x = x, color = factor(cluster))) +
stat_density(geom="line") + # draws kernel density estimate, which is a smoothed version of the histogram
# geom_histogram(binwidth = 1) +
theme_minimal()
x_density_plot <- ggplotly(x_density_plot) %>% layout(yaxis = list(showticklabels = FALSE, showgrid = FALSE), xaxis = list(showticklabels = FALSE, showgrid = FALSE))
y_density_plot <- df %>%
ggplot(aes(x = y, color = factor(cluster))) +
stat_density(geom="line") + # draws kernel density estimate, which is a smoothed version of the histogram
# geom_histogram(binwidth = 1) +
coord_flip() +
theme_minimal()
y_density_plot <- ggplotly(y_density_plot) %>% layout(yaxis = list(showticklabels = FALSE, showgrid = FALSE), xaxis = list(showticklabels = FALSE, showgrid = FALSE))
# https://ggplot2.tidyverse.org/reference/geom_quantile.html
qualtile_plot <- df %>%
ggplot(aes(x = x, y = y, color = factor(cluster))) +
geom_quantile(alpha = 0.8) + # fits a quantile regression to the data and draws the fitted quantiles with lines
theme_minimal()
qualtile_plot <- ggplotly(qualtile_plot) %>% layout(yaxis = list(showticklabels = FALSE, showgrid = FALSE))
Smoothing formula not specified. Using: y ~ x
Smoothing formula not specified. Using: y ~ x
# merge figures into one plot, via subplots, https://plotly-r.com/arranging-views.html
sub1 <- subplot(x_density_plot, plotly_empty(), point_plot, y_density_plot, nrows = 2, margin = 0, heights = c(0.15, 0.85), widths = c(0.9, 0.1), shareX = TRUE, shareY = TRUE, titleX = FALSE, titleY = FALSE) %>% layout()
No trace type specified and no positional attributes specifiedNo trace type specified:
Based on info supplied, a 'scatter' trace seems appropriate.
Read more about this trace type -> https://plotly.com/r/reference/#scatter
No scatter mode specifed:
Setting the mode to markers
Read more about this attribute -> https://plotly.com/r/reference/#scatter-mode
Can only have one: config
sub2 <- subplot(qualtile_plot, plotly_empty(), margin = 0, widths = c(0.9, 0.10), titleX = FALSE, titleY = FALSE) %>% layout()
No trace type specified and no positional attributes specifiedNo trace type specified:
Based on info supplied, a 'scatter' trace seems appropriate.
Read more about this trace type -> https://plotly.com/r/reference/#scatter
No scatter mode specifed:
Setting the mode to markers
Read more about this attribute -> https://plotly.com/r/reference/#scatter-mode
Can only have one: config
fig <- subplot(sub1, sub2, nrows = 2, margin = 0, heights = c(0.8, 0.2), shareX = TRUE) %>% layout(xaxis = list(title = name[1]), yaxis = list(title = name[2]))
Can only have one: config
fig
LS0tDQp0aXRsZTogImRpc2NvdmVyIGFuZCB0cmFuc2Zvcm0gcGxhc3RpYyB3YXN0ZSBtYWtlcnMgaW5kZXggZGF0YSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCi0tLQ0KcHVycG9zZSBvZiBub3RlYm9vaw0KLS0tDQoNCiAgKC0pIHRlc3QgYW5kIHBsYXkgd2l0aCBhZHZhbmNlZCBudW1lcmljYWwgRURBIG1ldGhvZHMNCiAgDQp0b2RvczoNCiAgKC0pIC4uLg0KICANCi0tLQ0KaW5mb3JtYXRpb24NCi0tLQ0KDQpuYW1lOiBtYWtlb3Zlcm1vbmRheV8yMDIxdzIyDQpsaW5rOiBodHRwczovL2RhdGEud29ybGQvbWFrZW92ZXJtb25kYXkvMjAyMXcyMg0KdGl0bGU6IDIwMjEvVzIyOiBUaGUgUGxhc3RpYyBXYXN0ZSBNYWtlcnMgSW5kZXgNCkRhdGEgU291cmNlOiBbTWluZGVyb29dKGh0dHBzOi8vd3d3Lm1pbmRlcm9vLm9yZy9wbGFzdGljLXdhc3RlLW1ha2Vycy1pbmRleC9kYXRhL2luZGljZXMvcHJvZHVjZXJzLykgZnJvbSAyMDE5DQogIA0KLS0tDQppbnNpZ2h0cyANCi0tLQ0KDQogIChpKSBjb3JyZWxhdGlvbiAtIG1vc3Qgb2YgdGhlIGNvbHVtbnMgYXJlIGhpZ2hseSBjb3JyZWxhdGVkLCB0aGF0IHdhcyB0byBiZSBleHBlY3RlZCwgc2luY2UgbW9zdCB2YXJpYWJsZXMgYXJlIGRlcGVuZCBvbiBlYWNoIG90aGVyLCBlLmcuLCByaWdpZCArIGZsZXhpYmxlID0gdG90YWwgYyBwcm9kdWN0aW9uLCAgICAgICAgICAgICAgICAgICAgICAgICB0b3RhbCAtPiAtcmFuaw0KICAgICAgICAgICAgICAgICAgICByaWdpZCBvdmVyYWxsIGhhcyBhIGxlc3Mgc3Ryb25nIGNvcnJlbGF0aW9uIHdpdGggdGhlIG90aGVyIHZhcmlhYmxlcywgd2hpY2ggbWlnaHQgaGludCB0byB0aGVyZSBiZWluZyBhIGEgZGlmZmVyZW50IHN1Yi1wb3B1bGF0aW9uIGJhc2VkIG9uIHJpZ2lkIHByb2R1Y3Rpb24sIA0KICAgICAgICAgICAgICAgICAgICBmbGV4aWJsZSBoYXMgYSBzdHJvbmdlciBjb3JyZWxhdGlvbiB3aXRoIHRvdGFsIGFzIHJpZ2lkIGFuZCB0b3RhbCwgc2luY2UgZmxleGlibGUgaXMgYSBmYXIgYmlnZ2VyIGNvbnRyaWJ1dGlvbiB0byB0b3RhbA0KICAgICAgICAgICAgICAgICAgICBmbGV4aWJsZSBoYXMgYSBzdHJvbmdlciBjb3JyZWxhdGlvbiB3aXRoIHRoZSBvdmVyYWxsIHByb2R1Y3Rpb24sIHRoYW4gcmlnaWQsIHRoaXMgaXMgaW50ZXJlc3RpbmcgYW5kIGFsc28gbWlnaHQgaGludCB0byByaWdpZCBwcm9kdWNlcnMgaGF2ZSBhIGRpZmZlcmVudCBtYXJrZXQgICAgICAgICAgICAgICAgICAgICAgICBzdHJhdGVneSB0aGFuIGZsZXhpYmxlIHByb2R1Y2Vycw0KICAoaSkgYSBHaW5pIGNvZWZmaWNpZW50IG9mIDAuNTYgaXMgcXVpdGUgaGlnaCwgd2hpY2ggbWVhbnMgdGhhdCBvbmx5IGEgdG9wIGZldyBwcm9kdWNlcnMgYXJlIHJlc3BvbnNpYmxlIGZvciBhIGxhcmdlIGFtb3VudCBvZiB0aGUgdG90YWwgU1VQIHdhc3RlIGNvbnRyaWJ1dGlvbiwgaW4gb3RoZXIgd29yZHMgMTAlICAgICAgICAgYXJlIHJlc3BvbnNpYmxlIGZvciA0NCUgb2YgdGhlIHdhc3RlDQogIA0KLS0tDQpsb2FkIHBhY2thZ2VzDQotLS0NCmBgYHtyIGxvYWQgcGFja2FnZXMsIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpICMgdGlkeSBkYXRhIGZyYW1lDQpsaWJyYXJ5KGdndGhlbWVzKSAjIGZvciBleHRyYSBwbG90IHRoZW1lcw0KbGlicmFyeShwbG90bHkpICMgbWFrZSBnZ3Bsb3RzIGludGVyYWN0aXZlDQoNCiMgaW5kaXZpZHVhbCBsaWJyYXJpZXMgYXJlIGluIHRoZSBhY2NvcmRpbmcgY2VsbA0KYGBgDQoNCi0tLQ0Kb3ZlcnZpZXcNCi0tLQ0KYGBge3J9DQpoZWFkKHBsYXN0aWMpDQpgYGANCmBgYHtyfQ0Kc3VtbWFyeShwbGFzdGljKQ0KYGBgDQoNCi0tLQ0KY29ycmVsYXRpb24gDQotLS0NCm1vc3Qgb2YgdGhlIGNvbHVtbnMgYXJlIGhpZ2hseSBjb3JyZWxhdGVkLCB0aGF0IHdhcyB0byBiZSBleHBlY3RlZCwgc2luY2UgbW9zdCB2YXJpYWJsZXMgYXJlIGRlcGVuZCBvbiBlYWNoIG90aGVyLCBlLmcuLCByaWdpZCArIGZsZXhpYmxlID0gdG90YWwgYyBwcm9kdWN0aW9uLCB0b3RhbCAtPiAtcmFuaw0KcmlnaWQgb3ZlcmFsbCBoYXMgYSBsZXNzIHN0cm9uZyBjb3JyZWxhdGlvbiB3aXRoIHRoZSBvdGhlciB2YXJpYWJsZXMsIHdoaWNoIG1pZ2h0IGhpbnQgdG8gdGhlcmUgYmVpbmcgYSBhIGRpZmZlcmVudCBzdWItcG9wdWxhdGlvbiBiYXNlZCBvbiByaWdpZCBwcm9kdWN0aW9uLCANCmZsZXhpYmxlIGhhcyBhIHN0cm9uZ2VyIGNvcnJlbGF0aW9uIHdpdGggdG90YWwgYXMgcmlnaWQgYW5kIHRvdGFsLCBzaW5jZSBmbGV4aWJsZSBpcyBhIGZhciBiaWdnZXIgY29udHJpYnV0aW9uIHRvIHRvdGFsDQpmbGV4aWJsZSBoYXMgYSBzdHJvbmdlciBjb3JyZWxhdGlvbiB3aXRoIHRoZSBvdmVyYWxsIHByb2R1Y3Rpb24sIHRoYW4gcmlnaWQsIHRoaXMgaXMgaW50ZXJlc3RpbmcgYW5kIGFsc28gbWlnaHQgaGludCB0byByaWdpZCBwcm9kdWNlcnMgaGF2ZSBhIGRpZmZlcmVudCBtYXJrZXQgc3RyYXRlZ3kgdGhhbiBmbGV4aWJsZSBwcm9kdWNlcnMNCg0KJT4lIHNlbGVjdCgtcmFuaywgLXRvdGFsLCAtYXNzZXRzKSBjYW4gc2hvdyBhIG1vcmUgY2xlYXIgcGljdHVyZSBieSByZW1vdmluZyBkZXBlbmRlbnQgdmFyaWFibGVzDQoNCmBgYHtyfQ0KbmFtZSA9IGMoJycpDQpkZiA8LSBwbGFzdGljICU+JSBzZWxlY3QoLXBvbHltZXJfcHJvZHVjZXIsIC0gdG90YWxfd2FzdGVfZGl2X3Byb2R1Y3Rpb24pICU+JSBtdXRhdGUocmFuayA9IC1yYW5rKSAlPiUgIyBjaGFuZ2Ugc2lnbiBvZiByYW5rIHRvIG1ha2UgaXQgaW5jcmVhc2Ugd2l0aCB0aGUgZGVwZW5kZW50IHZhcmlhYmxlcw0KICByZW5hbWUoIGFzc2V0cyA9IG5vX29mX2Fzc2V0cywgDQogICAgICAgICAgcHJvZHVjdCA9IHByb2R1Y3Rpb25fb2ZfaW5fc2NvcGVfcG9seW1lcnMsIA0KICAgICAgICAgIGZsZXhpYmxlID0gZmxleGlibGVfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUsIA0KICAgICAgICAgIHJpZ2lkID0gcmlnaWRfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUsIA0KICAgICAgICAgIHRvdGFsID0gdG90YWxfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSkgDQoNCg0KbGlicmFyeShjb3JycGxvdCkgIyBjb3JyZWxhdGlvbiBwbG90cw0KIyBodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvY29ycnBsb3QvdmlnbmV0dGVzL2NvcnJwbG90LWludHJvLmh0bWwNCg0KY29yIDwtIGNvcihkZikNCmNvcl9tdGVzdCA8LSBjb3IubXRlc3QoZGYsIGNvbmYubGV2ZWwgPSAwLjk5KSAjIGNvbWJpbmluZyBjb3JyZWxvZ3JhbSB3aXRoIHNpZ25pZmljYW5jZSB0ZXN0DQpjb3JycGxvdChjb3IsIG1ldGhvZCA9ICJudW1iZXIiLCBvcmRlciA9ICdoY2x1c3QnLCBhZGRyZWN0ID0gMywgcC5tYXQgPSBjb3JfbXRlc3QkcCwgaW5zaWcgPSAicGNoIikgIyBvcmRlciA9IEFPRSwgRlBDLCBoY2x1c3QgKyBhZGRyZWN0DQoNCmNvcnJwbG90KGNvciwgcC5tYXQgPSBjb3JfbXRlc3QkcCwgbG93ID0gY29yX210ZXN0JGxvd0NJLCB1cHAgPSBjb3JfbXRlc3QkdXBwQ0ksIG9yZGVyID0gJ2hjbHVzdCcsIHNpZy5sZXZlbCA9IDAuMDEsIHRsLnBvcyA9ICdkJywgYWRkcmVjdCA9IDMsIHJlY3QuY29sID0gJ25hdnknLCBwbG90QyA9ICdyZWN0JywgY2wucG9zID0gJ24nLCBpbnNpZyA9ICJwLXZhbHVlIikNCmBgYA0KDQotLS0NCkxvcmVueiBjdXJ2ZSAmIEdpbmkgY29lZmZpY2llbnQNCi0tLQ0KYSBHaW5pIGNvZWZmaWNpZW50IG9mIDAuNTYgaXMgcXVpdGUgaGlnaCwgd2hpY2ggbWVhbnMgdGhhdCBvbmx5IGEgdG9wIGZldyBwcm9kdWNlcnMgYXJlIHJlc3BvbnNpYmxlIGZvciBhIGxhcmdlIGFtb3VudCBvZiB0aGUgdG90YWwgU1VQIHdhc3RlIGNvbnRyaWJ1dGlvbiwgaW4gb3RoZXIgd29yZHMgMTAlIGFyZSByZXNwb25zaWJsZSBmb3IgNDQlIG9mIHRoZSB3YXN0ZQ0KDQoNCmBgYHtyfQ0KbmFtZSA9IGMoJ3RvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUnLCAnUHJvZHVjZXJzJykNCmRmIDwtIHBsYXN0aWMgJT4lIHJlbmFtZSh2YWx1ZSA9IHRvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpICU+JSBzZWxlY3QodmFsdWUpIA0KDQpsaWJyYXJ5KGdnbG9yZW56KSAjdHJhbnNmb3JtYXRpb25zIGZvciBwbG90dGluZyBMb3JlbnogY3VydmUsIGh0dHBzOi8vZ2l0aHViLmNvbS9qamNoZXJuL2dnbG9yZW56DQoNCmxvcmVuemN1cnZlIDwtIGRmICU+JSANCiAgZ2dwbG90KGFlcyh2YWx1ZSkpICsNCiAgICBzdGF0X2xvcmVueihkZXNjID0gRkFMU0UpICsNCiAgICBjb29yZF9maXhlZCgpICsNCiAgICBnZW9tX2FibGluZShsaW5ldHlwZSA9ICdkYXNoZWQnKSArDQogICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICBocmJydGhlbWVzOjpzY2FsZV94X3BlcmNlbnQoKSArDQogICAgaHJicnRoZW1lczo6c2NhbGVfeV9wZXJjZW50KCkgKw0KICAgICMgaHJicnRoZW1lczo6dGhlbWVfaXBzdW1fcmMoKSArDQogICAgYW5ub3RhdGVfaW5lcShkZiR2YWx1ZSkgKw0KICAgIGdndGl0bGUocGFzdGUoIkxvcmVueiBjdXJ2ZSBmb3IiLCBuYW1lWzFdLCBzZXA9IiAiKSkgDQpsb3JlbnpjdXJ2ZSA8LSBnZ3Bsb3RseShsb3JlbnpjdXJ2ZSkgJT4lIGxheW91dCh5YXhpcyA9IGxpc3QodGl0bGUgPSBwYXN0ZSgiY3VtdWxhdGl2ZSBwZXJjZW50YWdlIG9mIiwgbmFtZVsxXSwgc2VwPSIgIikpLCB4YXhpcyA9IGxpc3QodGl0bGUgPSBwYXN0ZSgiY3VtdWxhdGl2ZSBwZXJjZW50YWdlIG9mIiwgbmFtZVsyXSwgc2VwPSIgIikpKSANCg0KbG9yZW56Y3VydmUNCmBgYA0KYGBge3J9DQpuYW1lID0gYygnZmxleGlibGVfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUnLCAncmlnaWRfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUnLCAnUHJvZHVjZXJzJywgJ2ZsZXhpYmxlJywgJ3JpZ2lkJykNCmRmIDwtIHBsYXN0aWMgJT4lIHJlbmFtZShmbGV4aWJsZSA9IGZsZXhpYmxlX2Zvcm1hdF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlLCByaWdpZCA9IHJpZ2lkX2Zvcm1hdF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlKSAlPiUgc2VsZWN0KGZsZXhpYmxlLCByaWdpZCkgJT4lIHBpdm90X2xvbmdlcihjb2xzID0gYyhmbGV4aWJsZSxyaWdpZCkpDQoNCmxpYnJhcnkoZ2dsb3JlbnopICN0cmFuc2Zvcm1hdGlvbnMgZm9yIHBsb3R0aW5nIExvcmVueiBjdXJ2ZSwgaHR0cHM6Ly9naXRodWIuY29tL2pqY2hlcm4vZ2dsb3JlbnoNCg0KIyBnZXQgZ2dwbG90IHN0YW5kYXJkIGNvbG9ycyBmb3IgZ3JvdXBpbmcsIHdoaWNoIGFyZSBlcXVhbGx5IHNwYWNlZCBodWVzIGFyb3VuZCB0aGUgY29sb3Igd2hlZWwsIHN0YXJ0aW5nIGZyb20gMTUNCmdnX2NvbG9yX2h1ZSA8LSBmdW5jdGlvbihuKSB7DQogIGh1ZXMgPSBzZXEoMTUsIDM3NSwgbGVuZ3RoID0gbiArIDEpDQogIGhjbChoID0gaHVlcywgbCA9IDY1LCBjID0gMTAwKVsxOm5dDQp9DQoNCmxvcmVuemN1cnZlIDwtIGRmICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWUsIGNvbG9yID0gbmFtZSkpICsNCiAgICBzdGF0X2xvcmVueihkZXNjID0gRkFMU0UpICsNCiAgICBjb29yZF9maXhlZCgpICsNCiAgICBnZW9tX2FibGluZShsaW5ldHlwZSA9ICdkYXNoZWQnKSArDQogICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICBocmJydGhlbWVzOjpzY2FsZV94X3BlcmNlbnQoKSArDQogICAgaHJicnRoZW1lczo6c2NhbGVfeV9wZXJjZW50KCkgKw0KICAgICMgaHJicnRoZW1lczo6dGhlbWVfaXBzdW1fcmMoKSArDQogICAgYW5ub3RhdGVfaW5lcShmaWx0ZXIoZGYsIG5hbWUgPT0gbmFtZVs0XSkkdmFsdWUsIHkgPSAwLjk1LCBjb2xvdXIgPSBnZ19jb2xvcl9odWUoMilbMV0pICsNCiAgICBhbm5vdGF0ZV9pbmVxKGZpbHRlcihkZiwgbmFtZSA9PSBuYW1lWzVdKSR2YWx1ZSwgeSA9IDAuOTAsIGNvbG91ciA9IGdnX2NvbG9yX2h1ZSgyKVsyXSkgKw0KICAgIGdndGl0bGUocGFzdGUoImNvbXBhcmUgTG9yZW56IGN1cnZlIG9mIiwgbmFtZVsxXSwgImFuZCIsIG5hbWVbMl0sIHNlcD0iICIpKSANCmxvcmVuemN1cnZlIDwtIGdncGxvdGx5KGxvcmVuemN1cnZlKSAlPiUgbGF5b3V0KHlheGlzID0gbGlzdCh0aXRsZSA9IHBhc3RlKCJjdW11bGF0aXZlIHBlcmNlbnRhZ2Ugb2Y8YnI+IiwgbmFtZVsxXSwgIjxicj4iLCBuYW1lWzJdLCBzZXA9IiIpKSwgeGF4aXMgPSBsaXN0KHRpdGxlID0gcGFzdGUoImN1bXVsYXRpdmUgcGVyY2VudGFnZSBvZiIsIG5hbWVbMl0sIHNlcD0iICIpKSkgDQoNCmxvcmVuemN1cnZlDQpgYGANCg0KLS0tDQpwcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIGNvbG9yZWQgYnkgc2VsZiBvcmdhbml6aW5nIG1hcCBjbHVzdGVyDQotLS0NCkkgbmVlZCBtb3JlIGtub3dsZWRnZSBob3cgdG8gd29yayB3aXRoIGFuZCBpbnRlcnByZXQgU09NIGFuZCBQQ0EsIG1heWJlIGFsc28gbm90IGVub3VnaCBvYnNlcnZhdGlvbnMgaW4gZGF0YSBzZXQNCmh0dHBzOi8vaWFtY2llcmEuZ2l0aHViLmlvL1NPTWV4YW1wbGUvaHRtbC9TT01fUk5Bc2VxX3R1dG9yaWFsX3BhcnQyYV9TT00uaHRtbA0KDQpgYGB7cn0NCm5hbWUgPSBjKCdwb2x5bWVyX3Byb2R1Y2VyJykNCmRmIDwtIHBsYXN0aWMgJT4lIHNlbGVjdCgtIHRvdGFsX3dhc3RlX2Rpdl9wcm9kdWN0aW9uLCAtcmFuaywgLW5vX29mX2Fzc2V0cywgLXRvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpICU+JSAjIHJlbW92ZWQgdmFyaWFibGVzIHdoaWNoIGFyZSBkZXBlbmRlZCBvbiBlYWNoIG90aGVyDQogIHJlbmFtZSggcHJvZHVjdCA9IHByb2R1Y3Rpb25fb2ZfaW5fc2NvcGVfcG9seW1lcnMsIA0KICAgICAgICAgIGZsZXhpYmxlID0gZmxleGlibGVfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUsIA0KICAgICAgICAgIHJpZ2lkID0gcmlnaWRfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpDQoNCg0KbGlicmFyeShrb2hvbmVuKSAjIGZ1bmN0aW9ucyB0byB0cmFpbiBzZWxmLW9yZ2FuaXNpbmcgbWFwcyAoU09NcykNCg0KIyBzZXR1cCBmb3IgcGNhDQpzY2FsZV9kYXRhIDwtIGFzLm1hdHJpeCh0KHNjYWxlKHQoZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0pKSkpICMgV2UgbmVlZCB0byBub3JtYWxpemUgdGhlIGRhdGEgYmFzZWQgb24gc2NhbGUgZnVuY3Rpb24gYmVjYXVzZSB0aGUgdmFyaWFibGVzIGFyZSBkaWZmZXJlbnQgc2NhbGVzOyBOb3JtYWxpemF0aW9uIG1lYW5zIHN1YnRyYWN0aW5nIG1lYW4gZnJvbSBlYWNoIG9ic2VydmF0aW9uIGFuZCBkaXZpZGluZyB3aXRoIHN0YW5kYXJkIGRldmlhdGlvbi4gQ2hlY2sgYWxsIHRoZSB2YXJpYWJsZXMgbWVhbiB2YWx1ZXMgYXJlIHplcm8gbm93DQpoZWFkKHNjYWxlX2RhdGEpDQoNCiMgcHJpbmNpcGxlIGNvbXBvbmVudCBhbmFseXNpcw0KcGNhIDwtIHByY29tcChzY2FsZV9kYXRhLCBzY2FsZT1UUlVFKQ0Kc3VtbWFyeShwY2EpIA0KDQojIGFkZCBiYWNrIHRvIG9yaWdpbmFsIHNvIGV2ZXJ5dGhpbmcgaXMgdG9nZXRoZXINCnBjYV9zY29yZXMgPC0gZGF0YS5mcmFtZShwY2EkeCkNCmRhdGFfdmFsIDwtIGNiaW5kKGRmLCBwY2Ffc2NvcmVzKQ0KDQpwY2FfcGxvdCA8LSBnZ3Bsb3QoZGF0YV92YWwsIGFlcyh4ID0gUEMxLCB5ID0gUEMyKSkgKw0KICAgIGdlb21fcnVnKGFscGhhID0gMC41KSArICMgdHdvIDFkIG1hcmdpbmFsIGRpc3RyaWJ1dGlvbnMsIGRpc3BsYXkgaW5kaXZpZHVhbCBjYXNlcyBzbyBhcmUgYmVzdCB1c2VkIHdpdGggc21hbGxlciBkYXRhc2V0cw0KICAgIGdlb21fZGVuc2l0eV8yZChhbHBoYSA9IDAuMiwgYmlucyA9IDQpICsjIDJEIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRpb24gdXNpbmcgTUFTUzo6a2RlMmQoKSBhbmQgZGlzcGxheSB0aGUgcmVzdWx0cyB3aXRoIGNvbnRvdXJzDQogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNzUpICsgIyBwb2ludCBnZW9tIGlzIHVzZWQgdG8gY3JlYXRlIHNjYXR0ZXJwbG90cw0KICAgIHRoZW1lX21pbmltYWwoKQ0KcGNhX3Bsb3QgPC0gZ2dwbG90bHkocGNhX3Bsb3QpICU+JSBsYXlvdXQoKQ0KDQpwY2FfcGxvdA0KDQojIGNsdXN0ZXJpbmcgaXMgcGVyZm9ybWVkIHVzaW5nIHRoZSBzb20oKSBmdW5jdGlvbiBvbiB0aGUgc2NhbGVkIGdlbmUgZXhwcmVzc2lvbiB2YWx1ZXMuDQpzZXQuc2VlZCgzKQ0KDQojIGRlZmluZSBhIGdyaWQgZm9yIHRoZSBTT00gYW5kIHRyYWluDQpncmlkX3NpemUgPC0gbmNvbChzY2FsZV9kYXRhKQ0Kc29tX2dyaWQgPC0gc29tZ3JpZCh4ZGltID0gZ3JpZF9zaXplLCB5ZGltID0gZ3JpZF9zaXplLCB0b3BvID0gJ2hleGFnb25hbCcpDQpzb21fbW9kZWwgPC0gc29tKHNjYWxlX2RhdGEsIGdyaWQgPSBzb21fZ3JpZCkNCnN1bW1hcnkoc29tX21vZGVsKQ0KDQojIGdlbmVyYXRlIHNvbSBwbG90cyBhZnRlciB0cmFpbmluZw0KcGxvdChzb21fbW9kZWwsIHR5cGUgPSAnbWFwcGluZycpDQpwbG90KHNvbV9tb2RlbCwgdHlwZSA9ICdjb2RlcycpDQojIHBsb3Qoc29tX21vZGVsLCB0eXBlID0gJ2NvdW50cycpDQojIHBsb3Qoc29tX21vZGVsLCB0eXBlID0gJ2Rpc3QubmVpZ2hib3VycycpDQojIHBsb3Qoc29tX21vZGVsLCB0eXBlID0gJ3F1YWxpdHknKQ0KIyBwbG90KHNvbV9tb2RlbCwgdHlwZSA9ICdjaGFuZ2VzJykNCg0KIyBmdXJ0aGVyIHNwbGl0IHRoZSBjbHVzdGVycyBpbnRvIGEgc21hbGxlciBzZXQgb2YgY2x1c3RlcnMgdXNpbmcgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcuDQpzb21fY2x1c3RlciA8LSBjdXRyZWUoaGNsdXN0KGRpc3Qoc29tX21vZGVsJGNvZGVzW1sxXV0pKSwgMikgIyB1c2UgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgdG8gY2x1c3RlciB0aGUgY29kZWJvb2sgdmVjdG9ycw0KDQpwbG90KHNvbV9tb2RlbCwgdHlwZT0ibWFwcGluZyIsIGJnY29sID0gc29tX2NsdXN0ZXIsIG1haW4gPSAiQ2x1c3RlcnMiKQ0KYWRkLmNsdXN0ZXIuYm91bmRhcmllcyhzb21fbW9kZWwsIHNvbV9jbHVzdGVyKQ0KDQojIGF0dGFjaCB0aGUgaGllcmNoYWwgY2x1c3RlciB0byB0aGUgbGFyZ2VyIGRhdGFzZXQgZGF0YV92YWwuDQpncmlkU3F1YXJlIDwtIGdyaWRfc2l6ZSAqIGdyaWRfc2l6ZQ0Kc29tX2NsdXN0ZXJLZXkgPC0gZGF0YS5mcmFtZShzb21fY2x1c3RlcikNCnNvbV9jbHVzdGVyS2V5JHVuaXRfY2xhc3NpZiA8LSBjKDE6Z3JpZFNxdWFyZSkNCmRhdGFfdmFsIDwtIGNiaW5kKGRhdGFfdmFsLHNvbV9tb2RlbCR1bml0LmNsYXNzaWYsc29tX21vZGVsJGRpc3RhbmNlcykgICU+JSByZW5hbWUodW5pdF9jbGFzc2lmID0gJ3NvbV9tb2RlbCR1bml0LmNsYXNzaWYnLCBkaXN0YW5jZXMgPSAnc29tX21vZGVsJGRpc3RhbmNlcycpDQpkYXRhX3ZhbCA8LSBtZXJnZShkYXRhX3ZhbCwgc29tX2NsdXN0ZXJLZXksIGJ5LnggPSAidW5pdF9jbGFzc2lmIiApDQpoZWFkKGRhdGFfdmFsKQ0KDQojIHBsb3QgcGNhIHdpdGggY29sb3JlZCBjbHVzdGVycw0KcGNhc29tX3Bsb3QgPC0gZ2dwbG90KGRhdGFfdmFsLCBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3IgPSBmYWN0b3Ioc29tX2NsdXN0ZXIpLCB0ZXh0ID0gcG9seW1lcl9wcm9kdWNlcikpICsNCiAgICBnZW9tX3J1ZyhhbHBoYSA9IDAuNSkgKyAjIHR3byAxZCBtYXJnaW5hbCBkaXN0cmlidXRpb25zLCBkaXNwbGF5IGluZGl2aWR1YWwgY2FzZXMgc28gYXJlIGJlc3QgdXNlZCB3aXRoIHNtYWxsZXIgZGF0YXNldHMNCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC43NSkgKyAjIHBvaW50IGdlb20gaXMgdXNlZCB0byBjcmVhdGUgc2NhdHRlcnBsb3RzDQogICAgdGhlbWVfbWluaW1hbCgpDQpwY2Fzb21fcGxvdCA8LSBnZ3Bsb3RseShwY2Fzb21fcGxvdCkgJT4lIGxheW91dCgpDQoNCnBjYXNvbV9wbG90DQpgYGANCg0KYGBge3J9DQojIHR3byB2YXJpYWJsZXMsIGNvbnRpbnVvdXMgeCwgY29udGludW91cyB5LCBzaG93IHRyZW5kIGFuZCBkaXN0cmlidXRpb24NCm5hbWUgPSBjKCdwcm9kdWN0aW9uX29mX2luX3Njb3BlX3BvbHltZXJzJywgJ3RvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUnKQ0KZGYgPC0gbWVyZ2UocGxhc3RpYywgZGF0YV92YWwsIGJ5LnggPSAncG9seW1lcl9wcm9kdWNlcicpDQpkZiA8LSBkZiAlPiUgcmVuYW1lKHggPSBwcm9kdWN0aW9uX29mX2luX3Njb3BlX3BvbHltZXJzLCB5ID0gdG90YWxfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSwgY2x1c3RlciA9IHNvbV9jbHVzdGVyLCB0ZXh0ID0gcG9seW1lcl9wcm9kdWNlcikgJT4lIHNlbGVjdCh4LCB5LCBjbHVzdGVyLCB0ZXh0KSANCg0KIyBodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ2VvbV9zbW9vdGguaHRtbA0KcG9pbnRfcGxvdCA8LSBkZiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0geCwgeSA9IHksIGNvbG9yID0gZmFjdG9yKGNsdXN0ZXIpKSkgKw0KICAgICMgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjUsIHNpemUgPSAxKSArDQogICAgZ2VvbV9ydWcoYWxwaGEgPSAwLjUpICsgIyB0d28gMWQgbWFyZ2luYWwgZGlzdHJpYnV0aW9ucywgZGlzcGxheSBpbmRpdmlkdWFsIGNhc2VzIHNvIGFyZSBiZXN0IHVzZWQgd2l0aCBzbWFsbGVyIGRhdGFzZXRzDQogICAgZ2VvbV9kZW5zaXR5XzJkKGFscGhhID0gMC4yLCBiaW5zID0gNCkgKyMgMkQga2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbiB1c2luZyBNQVNTOjprZGUyZCgpIGFuZCBkaXNwbGF5IHRoZSByZXN1bHRzIHdpdGggY29udG91cnMNCiAgICBnZW9tX3Ntb290aChmaWxsID0gImdyZXk5MCIpICsgIyBhaWRzIHRoZSBleWUgaW4gc2VlaW5nIHBhdHRlcm5zIGluIHRoZSBwcmVzZW5jZSBvZiBvdmVycGxvdHRpbmcNCiAgICBnZW9tX3BvaW50KGFlcyh0ZXh0ID0gdGV4dCksIGFscGhhID0gMC43NSkgKyAjIHBvaW50IGdlb20gaXMgdXNlZCB0byBjcmVhdGUgc2NhdHRlcnBsb3RzDQogICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICBnZ3RpdGxlKHBhc3RlKCJ0cmVuZCBvZiIsIG5hbWVbMl0sICJvdmVyIiwgbmFtZVsxXSwgc2VwPSIgIikpIA0KcG9pbnRfcGxvdCA8LSBnZ3Bsb3RseShwb2ludF9wbG90KSAlPiUgbGF5b3V0KHhheGlzID0gbGlzdChzaG93dGlja2xhYmVscyA9IEZBTFNFKSkNCg0KeF9kZW5zaXR5X3Bsb3QgPC0gZGYgJT4lDQogIGdncGxvdChhZXMoeCA9IHgsIGNvbG9yID0gZmFjdG9yKGNsdXN0ZXIpKSkgKw0KICAgIHN0YXRfZGVuc2l0eShnZW9tPSJsaW5lIikgKyAjIGRyYXdzIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRlLCB3aGljaCBpcyBhIHNtb290aGVkIHZlcnNpb24gb2YgdGhlIGhpc3RvZ3JhbQ0KICAgICMgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKSArDQogICAgdGhlbWVfbWluaW1hbCgpIA0KeF9kZW5zaXR5X3Bsb3QgPC0gZ2dwbG90bHkoeF9kZW5zaXR5X3Bsb3QpICU+JSBsYXlvdXQoeWF4aXMgPSBsaXN0KHNob3d0aWNrbGFiZWxzID0gRkFMU0UsIHNob3dncmlkID0gRkFMU0UpLCB4YXhpcyA9IGxpc3Qoc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSwgc2hvd2dyaWQgPSBGQUxTRSkpDQoNCnlfZGVuc2l0eV9wbG90IDwtIGRmICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB5LCBjb2xvciA9IGZhY3RvcihjbHVzdGVyKSkpICsNCiAgICBzdGF0X2RlbnNpdHkoZ2VvbT0ibGluZSIpICsgIyBkcmF3cyBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZSwgd2hpY2ggaXMgYSBzbW9vdGhlZCB2ZXJzaW9uIG9mIHRoZSBoaXN0b2dyYW0NCiAgICAjIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkgKw0KICAgIGNvb3JkX2ZsaXAoKSArDQogICAgdGhlbWVfbWluaW1hbCgpIA0KeV9kZW5zaXR5X3Bsb3QgPC0gZ2dwbG90bHkoeV9kZW5zaXR5X3Bsb3QpICU+JSBsYXlvdXQoeWF4aXMgPSBsaXN0KHNob3d0aWNrbGFiZWxzID0gRkFMU0UsIHNob3dncmlkID0gRkFMU0UpLCB4YXhpcyA9IGxpc3Qoc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSwgc2hvd2dyaWQgPSBGQUxTRSkpDQoNCiMgaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21fcXVhbnRpbGUuaHRtbA0KcXVhbHRpbGVfcGxvdCA8LSBkZiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0geCwgeSA9IHksIGNvbG9yID0gZmFjdG9yKGNsdXN0ZXIpKSkgKw0KICAgIGdlb21fcXVhbnRpbGUoYWxwaGEgPSAwLjgpICsgIyBmaXRzIGEgcXVhbnRpbGUgcmVncmVzc2lvbiB0byB0aGUgZGF0YSBhbmQgZHJhd3MgdGhlIGZpdHRlZCBxdWFudGlsZXMgd2l0aCBsaW5lcw0KICAgIHRoZW1lX21pbmltYWwoKSANCnF1YWx0aWxlX3Bsb3QgPC0gZ2dwbG90bHkocXVhbHRpbGVfcGxvdCkgJT4lIGxheW91dCh5YXhpcyA9IGxpc3Qoc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSwgc2hvd2dyaWQgPSBGQUxTRSkpDQoNCiMgbWVyZ2UgZmlndXJlcyBpbnRvIG9uZSBwbG90LCB2aWEgc3VicGxvdHMsIGh0dHBzOi8vcGxvdGx5LXIuY29tL2FycmFuZ2luZy12aWV3cy5odG1sDQpzdWIxIDwtIHN1YnBsb3QoeF9kZW5zaXR5X3Bsb3QsIHBsb3RseV9lbXB0eSgpLCBwb2ludF9wbG90LCB5X2RlbnNpdHlfcGxvdCwgbnJvd3MgPSAyLCBtYXJnaW4gPSAwLCBoZWlnaHRzID0gYygwLjE1LCAwLjg1KSwgd2lkdGhzID0gYygwLjksIDAuMSksIHNoYXJlWCA9IFRSVUUsIHNoYXJlWSA9IFRSVUUsIHRpdGxlWCA9IEZBTFNFLCB0aXRsZVkgPSBGQUxTRSkgJT4lIGxheW91dCgpDQpzdWIyIDwtIHN1YnBsb3QocXVhbHRpbGVfcGxvdCwgcGxvdGx5X2VtcHR5KCksIG1hcmdpbiA9IDAsIHdpZHRocyA9IGMoMC45LCAwLjEwKSwgdGl0bGVYID0gRkFMU0UsIHRpdGxlWSA9IEZBTFNFKSAlPiUgbGF5b3V0KCkNCmZpZyA8LSBzdWJwbG90KHN1YjEsIHN1YjIsIG5yb3dzID0gMiwgbWFyZ2luID0gMCwgaGVpZ2h0cyA9IGMoMC44LCAwLjIpLCBzaGFyZVggPSBUUlVFKSAlPiUgbGF5b3V0KHhheGlzID0gbGlzdCh0aXRsZSA9IG5hbWVbMV0pLCB5YXhpcyA9IGxpc3QodGl0bGUgPSBuYW1lWzJdKSkNCiAgDQpmaWcNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=